package com.my.db.dataInsert;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.io.*;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * service_order 表水稻数据批量插入工具类（时间控制在当前时间以内）
 * 核心：基于水稻农事时间节点生成，所有时间均在当前时间（2025-09-21）之前
 * 特性：可配置插入量、随机生成联系人/电话、作物-图片关联、多线程批量插入、事务回滚
 * 特性：分批次处理、批量更新、事务回滚（存储处理数据的文件，回滚sql）、多线程处理、连接池管理
 */
public class ServiceOrderBatchInsert {

    // ============================ 1. 基础配置 ============================
    private static final Logger logger = LoggerFactory.getLogger(ServiceOrderBatchInsert.class);
    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private static final Random RANDOM = new Random();
    private static final SnowflakeIdGenerator ID_GENERATOR = new SnowflakeIdGenerator(1, 1); // ID生成器
    // 当前时间常量（2025-09-21），所有数据时间必须在此之前
    private static final Timestamp CURRENT_TIME;
    static {
        try {
            CURRENT_TIME = new Timestamp(DATE_FORMAT.parse("2025-09-21 00:00:00").getTime());
        } catch (ParseException e) {
            throw new RuntimeException("初始化当前时间失败", e);
        }
    }

    // ============================ 2. 数据库连接配置 ============================
    private static final String DB_URL = "jdbc:mysql://xxxx:6777/yilong?" +
            "useUnicode=true&characterEncoding=utf8" +
            "&autoReconnect=true" +
            "&zeroDateTimeBehavior=CONVERT_TO_NULL" +
            "&transformedBitIsBoolean=true" +
            "&allowPublicKeyRetrieval=true" +
            "&serverTimezone=Asia/Shanghai";
    private static final String DB_USER = "root";
    private static final String DB_PASSWORD = "11@2";
    private static DataSource dataSource;

    // ============================ 3. 插入参数配置 ============================
    private static final int TOTAL_INSERT_COUNT = 1054; // 总插入记录数
    private static final int BASE_NUM_PER_RECORD = 1; // 单条记录默认num
    private static final BigDecimal UNIT_PRICE = new BigDecimal("50.00"); // 单价
    private static final String DATA_FILE_PATH = "F:\\临时\\service_order_inserted_ids.txt"; // 回滚ID存储路径
    private static final int BATCH_SIZE = 500; // 每批次插入条数
    private static final int THREAD_POOL_SIZE = 4; // 线程池大小
    // 预计完成时间范围（使用这些天数）
    private static final int[] EXP_COM_TIME_DAYS = {1,2,3};

    // ============================ 4. 固定字段值 ============================
    private static final String CREATE_BY = "minAppName126";
    private static final String USER_ID = "1914857210854903810";
    private static final int ORDER_STATUS = 3;
    private static final int ALLOT_STATUS = 0;
    private static final int ORDER_TYPE = 1;
    private static final int SERVICE_PROJECT_TYPE = 1;
    private static final String SYS_ORG_CODE = "1780120784835211268";
    private static final String SERVICE_SUBJECT = "1780120784835211268";
    private static final int DEL_FLAG = 0;
    private static final List<String> COMMON_ADDRESSES = Arrays.asList(
            "双胜镇永盛路14号", "马鞍镇东风街8号", "仪陇县双胜镇永久村",
            "双胜镇火井村5组", "马鞍镇建设南路12号", "仪陇县金城镇文化路"
    );

    // ============================ 5. 核心映射：水稻-农事时间-图片 ============================
    // 5.1 作物类型（仅保留水稻）
    private static final List<String> CROPS = Arrays.asList("水稻");

    // 5.2 农事类型定义（仅保留需要的类型）
    private static final int FARMING_TYPE_PLOW = 1;      // 耕地
    private static final int FARMING_TYPE_PLANT = 2;    // 种植
    private static final int FARMING_TYPE_RICE_PROTECTION = 35; // 水稻植保
    private static final int FARMING_TYPE_HARVEST = 5;  // 采收

    // 5.3 水稻-农事时间-图片规则（扩大时间跨度，避免紧凑）
    private static Map<String, List<FarmingRule>> CROP_FARMING_RULES;

    // 农事规则实体
    @Data
    @AllArgsConstructor
    private static class FarmingRule {
        int farmingType;          // 农事类型
        Timestamp startTime;      // 农事开始时间
        Timestamp endTime;        // 农事结束时间（确保在当前时间之前）
        String imgPath;           // 农事对应的图片路径
        String serviceProject;    // 匹配的服务项目
        int protectionRepeat = 1; // 植保重复次数
    }

    // 5.4 水稻-服务项目映射
    private static final Map<String, String> CROP_PLANT_SERVICE = new HashMap<String, String>() {{
        put("水稻", "机播水稻");
    }};
    private static final Map<String, String> CROP_HARVEST_SERVICE = new HashMap<String, String>() {{
        put("水稻", "机收水稻");
    }};

    // 5.5 图片路径常量（仅保留水稻相关）
    private static final String IMG_PLOW = "https://nong.yaxingtech.com.cn:8006/api/jeecg-boot/sys/common/static/temp/耕地_1756193765604.png";
    private static final String IMG_RICE_PLANT = "https://nong.yaxingtech.com.cn:8006/api/jeecg-boot/sys/common/static/temp/种植_1756193797650.png";
    private static final String IMG_RICE_PROTECTION = "https://nong.yaxingtech.com.cn:8006/api/jeecg-boot/sys/common/static/temp/植保1_1756193813551.png";
    private static final String IMG_RICE_HARVEST = "https://nong.yaxingtech.com.cn:8006/api/jeecg-boot/sys/common/static/temp/123_1755599109582.png";

    // ============================ 6. 初始化 ============================
    static {
        // 初始化数据库连接池
        initDataSource();
        // 初始化水稻-农事时间规则（扩大时间跨度，避免紧凑）
        initCropFarmingRules();
    }

    /**
     * 初始化数据库连接池
     */
    private static void initDataSource() {
        try {
            HikariConfig config = new HikariConfig();
            config.setJdbcUrl(DB_URL);
            config.setUsername(DB_USER);
            config.setPassword(DB_PASSWORD);
            config.setMaximumPoolSize(THREAD_POOL_SIZE);
            config.setConnectionTimeout(30000);
            config.setIdleTimeout(600000);
            config.setMaxLifetime(1800000);
            config.setMinimumIdle(2);
            dataSource = new HikariDataSource(config);
            logger.info("数据库连接池初始化成功");
        } catch (Exception e) {
            logger.error("数据库连接池初始化失败", e);
            throw new RuntimeException("连接池初始化异常", e);
        }
    }

    /**
     * 初始化水稻-农事时间规则（扩大时间跨度，所有时间均在当前时间2025-09-21之前）
     */
    private static void initCropFarmingRules() {
        CROP_FARMING_RULES = new HashMap<>();

        try {
            // -------------------------- 水稻农事规则（扩大时间跨度，避免时间紧凑） --------------------------
            List<FarmingRule> riceRules = new ArrayList<>();

            // 1. 耕地（4月-5月，跨度1个多月）
            riceRules.add(new FarmingRule(
                    FARMING_TYPE_PLOW,
                    parseTime("2025-04-01 08:00:00"),
                    parseTime("2025-05-15 18:00:00"),
                    IMG_PLOW,
                    "机耕",
                    1
            ));

            // 2. 种植（5月下旬-6月下旬，跨度1个月）
            riceRules.add(new FarmingRule(
                    FARMING_TYPE_PLANT,
                    parseTime("2025-05-20 08:00:00"),
                    parseTime("2025-06-30 18:00:00"),
                    IMG_RICE_PLANT,
                    CROP_PLANT_SERVICE.get("水稻"),
                    1
            ));

            // 3. 水稻植保（7月-8月，跨度2个月，增加重复次数）
            riceRules.add(new FarmingRule(
                    FARMING_TYPE_RICE_PROTECTION,
                    parseTime("2025-07-01 08:00:00"),
                    parseTime("2025-08-31 18:00:00"),
                    IMG_RICE_PROTECTION,
                    "无人机打药",
                    3 // 增加植保次数，分散时间
            ));

            // 4. 采收（9月1日-9月15日，确保在当前时间之前）
            riceRules.add(new FarmingRule(
                    FARMING_TYPE_HARVEST,
                    parseTime("2025-09-01 08:00:00"),
                    parseTime("2025-09-15 18:00:00"),
                    IMG_RICE_HARVEST,
                    CROP_HARVEST_SERVICE.get("水稻"),
                    1
            ));

            CROP_FARMING_RULES.put("水稻", riceRules);

            // 验证所有时间是否在当前时间之前
            validateAllTimes();

            logger.info("水稻农事规则初始化完成，包含{}个农事阶段，所有时间均在当前时间之前", riceRules.size());
        } catch (ParseException e) {
            logger.error("解析农事时间失败", e);
            throw new RuntimeException("农事时间初始化异常", e);
        }
    }

    /**
     * 验证所有农事时间是否在当前时间之前
     */
    private static void validateAllTimes() {
        for (Map.Entry<String, List<FarmingRule>> entry : CROP_FARMING_RULES.entrySet()) {
            String crop = entry.getKey();
            for (FarmingRule rule : entry.getValue()) {
                if (rule.getStartTime().after(CURRENT_TIME)) {
                    throw new IllegalArgumentException("作物[" + crop + "]的农事开始时间在当前时间之后: " + rule.getStartTime());
                }
                if (rule.getEndTime().after(CURRENT_TIME)) {
                    throw new IllegalArgumentException("作物[" + crop + "]的农事结束时间在当前时间之后: " + rule.getEndTime());
                }
                if (rule.getStartTime().after(rule.getEndTime())) {
                    throw new IllegalArgumentException("作物[" + crop + "]的农事开始时间晚于结束时间");
                }
            }
        }
    }

    // ============================ 7. 工具方法 ============================
    /**
     * 解析时间字符串为Timestamp
     */
    private static Timestamp parseTime(String timeStr) throws ParseException {
        return new Timestamp(DATE_FORMAT.parse(timeStr).getTime());
    }

    /**
     * 在农事时间范围内随机生成create_time，通过扩大范围和随机算法避免紧凑
     */
    private static Timestamp randomCreateTimeInRange(Timestamp startTime, Timestamp endTime) {
        // 确保结束时间不晚于当前时间
        long endTimeMillis = Math.min(endTime.getTime(), CURRENT_TIME.getTime());
        long startTimeMillis = startTime.getTime();

        // 增加时间跨度，确保分布更均匀
        long timeRange = endTimeMillis - startTimeMillis;
        if (timeRange < 0) {
            startTimeMillis = endTimeMillis - 86400000 * 7; // 至少保留7天跨度
            timeRange = endTimeMillis - startTimeMillis;
        }

        // 使用更分散的随机算法，避免时间集中
        long randomOffset = (long) (RANDOM.nextDouble() * timeRange);
        // 增加随机性，避免连续生成相近时间
        randomOffset = (long) (randomOffset * (0.8 + RANDOM.nextDouble() * 0.4));

        return new Timestamp(startTimeMillis + randomOffset);
    }

    /**
     * 生成exp_com_time（使用EXP_COM_TIME_DAYS，确保在createTime之后）
     */
    private static Timestamp generateExpComTime(Timestamp createTime, Timestamp farmingEndTime) {
        // 1. 从配置的天数数组中随机选择
        int addDays = EXP_COM_TIME_DAYS[RANDOM.nextInt(EXP_COM_TIME_DAYS.length)];

        // 2. 计算基础预计完成时间（createTime + 随机天数）
        long baseExpTime = createTime.getTime() + (addDays * 24L * 60 * 60 * 1000);

        // 3. 计算最大允许时间（农事结束时间和当前时间的较早者）
        long maxAllowedTime = Math.min(farmingEndTime.getTime(), CURRENT_TIME.getTime());

        // 4. 确保预计完成时间在创建时间之后，且不超过最大允许时间
        long finalExpTime;
        if (baseExpTime > maxAllowedTime) {
            // 如果基础时间超过最大允许时间，使用最大允许时间，但必须确保在创建时间之后
            finalExpTime = Math.max(maxAllowedTime, createTime.getTime() + 3600 * 1000); // 至少比创建时间晚1小时
        } else {
            finalExpTime = baseExpTime;
        }

        // 5. 最后验证，确保预计完成时间一定在创建时间之后
        if (finalExpTime <= createTime.getTime()) {
            // 极端情况处理：强制设置为创建时间后1小时
            finalExpTime = createTime.getTime() + 3600 * 1000;
            logger.warn("调整异常时间：createTime={}, 修正后的expComTime={}",
                    createTime, new Timestamp(finalExpTime));
        }

        return new Timestamp(finalExpTime);
    }

    /**
     * 随机生成联系人（包含二字和三字姓名）
     */
    private static String randomContactPerson() {
        String[] surnames = {"李", "王", "张", "刘", "陈", "杨", "赵", "黄", "周", "吴", "徐", "孙", "胡", "朱", "高", "林", "何", "郭", "马"};
        String[] twoCharFirstNames = {"伟", "芳", "军", "洋", "敏", "静", "强", "丽", "磊", "婷", "明", "浩", "娟", "杰", "涛", "娜", "勇", "艳"};
        String[] threeCharFirstNames = {"建国", "卫东", "援朝", "红兵", "志强", "秀丽", "桂英", "建华", "淑芬", "永强", "秀兰", "大军", "小红", "婷婷", "晶晶"};

        // 50%概率生成二字姓名，50%概率生成三字姓名
        if (RANDOM.nextBoolean()) {
            return surnames[RANDOM.nextInt(surnames.length)] + twoCharFirstNames[RANDOM.nextInt(twoCharFirstNames.length)];
        } else {
            return surnames[RANDOM.nextInt(surnames.length)] + threeCharFirstNames[RANDOM.nextInt(threeCharFirstNames.length)];
        }
    }

    /**
     * 随机生成手机号
     */
    private static String randomPhone() {
        String[] prefixes = {"130", "131", "132", "133", "134", "135", "136", "137", "138", "139", "147", "150", "151", "152", "153", "155", "156", "157", "158", "159", "170", "171", "173", "175", "176", "177", "178", "180", "181", "182", "183", "184", "185", "186", "187", "188", "189", "198", "199"};
        StringBuilder suffix = new StringBuilder();
        for (int i = 0; i < 8; i++) suffix.append(RANDOM.nextInt(10)); // 注意：这里修正为8位，因为前缀已经3位，总共11位
        return prefixes[RANDOM.nextInt(prefixes.length)] + suffix;
    }

    /**
     * 雪花算法ID生成器
     */
    private static class SnowflakeIdGenerator {
        private final long workerId;
        private final long datacenterId;
        private long sequence = 0L;
        private long lastTimestamp = -1L;

        public SnowflakeIdGenerator(long workerId, long datacenterId) {
            if (workerId > 31 || workerId < 0) throw new IllegalArgumentException("workerId必须在0-31之间");
            if (datacenterId > 31 || datacenterId < 0) throw new IllegalArgumentException("datacenterId必须在0-31之间");
            this.workerId = workerId;
            this.datacenterId = datacenterId;
        }

        public synchronized String nextId() {
            long timestamp = System.currentTimeMillis();
            if (timestamp < lastTimestamp) throw new RuntimeException("时钟回拨，拒绝生成ID");
            if (lastTimestamp == timestamp) {
                sequence = (sequence + 1) & 4095;
                if (sequence == 0) timestamp = nextMillis(lastTimestamp);
            } else sequence = 0L;
            lastTimestamp = timestamp;
            long id = ((timestamp - 1609459200000L) << 22) | (datacenterId << 17) | (workerId << 12) | sequence;
            return String.valueOf(id);
        }

        private long nextMillis(long lastTimestamp) {
            long timestamp = System.currentTimeMillis();
            while (timestamp <= lastTimestamp) timestamp = System.currentTimeMillis();
            return timestamp;
        }
    }

    // ============================ 8. 核心数据生成逻辑 ============================
    /**
     * 生成待插入的水稻订单数据列表（所有时间均在当前时间之前，时间分布更分散）
     */
    private static List<OrderInsertData> generateInsertDataList() {
        List<OrderInsertData> dataList = new ArrayList<>(TOTAL_INSERT_COUNT);
        String crop = "水稻";
        List<FarmingRule> farmingRules = CROP_FARMING_RULES.get(crop);

        if (farmingRules == null || farmingRules.isEmpty()) {
            logger.error("水稻无农事规则，无法生成数据");
            return dataList;
        }

        // 按农事规则分配记录数（考虑植保重复次数）
        int totalProtectionRepeat = 0;
        for (FarmingRule rule : farmingRules) {
            totalProtectionRepeat += rule.getProtectionRepeat();
        }
        int basePerRule = TOTAL_INSERT_COUNT / totalProtectionRepeat;
        int remainder = TOTAL_INSERT_COUNT % totalProtectionRepeat;

        int ruleIndex = 0;
        for (FarmingRule rule : farmingRules) {
            // 计算当前农事的记录数（包含重复次数）
            int ruleTotalCount = (ruleIndex < remainder) ? basePerRule + 1 : basePerRule;
            ruleTotalCount *= rule.getProtectionRepeat();
            ruleIndex++;

            // 生成当前农事的订单数据
            for (int j = 0; j < ruleTotalCount; j++) {
                // 基础字段
                String id = ID_GENERATOR.nextId();
                String contactPerson = randomContactPerson();
                String phone = randomPhone();
                String address = COMMON_ADDRESSES.get(RANDOM.nextInt(COMMON_ADDRESSES.size()));
                int num = BASE_NUM_PER_RECORD;

                // 时间字段（在扩大的农事时间范围内生成，确保分散）
                Timestamp createTime = randomCreateTimeInRange(rule.getStartTime(), rule.getEndTime());
                Timestamp expComTime = generateExpComTime(createTime, rule.getEndTime());
                // 增加时间顺序验证
                if (expComTime.before(createTime)) {
                    logger.error("时间顺序异常：createTime={}, expComTime={}", createTime, expComTime);
                    throw new RuntimeException("生成了预计完成时间早于创建时间的数据");
                }
                // 再次验证时间是否符合要求
                if (createTime.after(CURRENT_TIME) || expComTime.after(CURRENT_TIME)) {
                    logger.error("生成了未来时间的数据，createTime: {}, expComTime: {}, 当前时间: {}",
                            createTime, expComTime, CURRENT_TIME);
                    throw new RuntimeException("生成了未来时间的数据");
                }

                // 金额字段
                BigDecimal totalAmount = UNIT_PRICE.multiply(new BigDecimal(num));

                // 封装数据
                dataList.add(new OrderInsertData(
                        id, contactPerson, phone, address, num,
                        rule.getServiceProject(), crop,
                        rule.getImgPath().isEmpty() ? null : rule.getImgPath(),
                        createTime, expComTime, totalAmount
                ));

                // 达到总记录数则停止
                if (dataList.size() >= TOTAL_INSERT_COUNT) break;
            }
            if (dataList.size() >= TOTAL_INSERT_COUNT) break;
        }

        // 若总记录数不足，补充到TOTAL_INSERT_COUNT
        while (dataList.size() < TOTAL_INSERT_COUNT) {
            FarmingRule rule = farmingRules.get(RANDOM.nextInt(farmingRules.size()));
            Timestamp createTime = randomCreateTimeInRange(rule.getStartTime(), rule.getEndTime());
            Timestamp expComTime = generateExpComTime(createTime, rule.getEndTime());

            dataList.add(new OrderInsertData(
                    ID_GENERATOR.nextId(),
                    randomContactPerson(),
                    randomPhone(),
                    COMMON_ADDRESSES.get(RANDOM.nextInt(COMMON_ADDRESSES.size())),
                    BASE_NUM_PER_RECORD,
                    rule.getServiceProject(),
                    crop,
                    rule.getImgPath().isEmpty() ? null : rule.getImgPath(),
                    createTime,
                    expComTime,
                    UNIT_PRICE.multiply(new BigDecimal(BASE_NUM_PER_RECORD))
            ));
        }

        logger.info("水稻待插入数据生成完成，共{}条（目标{}条），所有时间均在当前时间之前",
                dataList.size(), TOTAL_INSERT_COUNT);
        return dataList;
    }

    // ============================ 9. 批量插入与回滚逻辑 ============================
    /**
     * 订单数据实体
     */
    @Data
    @AllArgsConstructor
    private static class OrderInsertData {
        private String id;
        private String contactPerson;
        private String phone;
        private String address;
        private int num;
        private String serviceProject;
        private String crop;
        private String shopImg;
        private Timestamp createTime;
        private Timestamp expComTime;
        private BigDecimal totalAmount;
    }

    /**
     * 处理单个批次插入
     */
    private static int processInsertBatch(List<OrderInsertData> batchData, BufferedWriter writer) throws SQLException, IOException {
        if (batchData.isEmpty()) return 0;

        // 插入SQL
        String insertSql = "INSERT INTO `yilong`.`service_order` (" +
                "`id`, `goods_id`, `specification`, `allot_status`, `agricultural_machinery_id`, " +
                "`goods_shopping_car_uid`, `leave_message`, `goods_address_id`, `create_by`, `create_time`, " +
                "`update_by`, `update_time`, `sys_org_code`, `service_project`, `order_no`, " +
                "`order_type`, `order_status`, `send_status`, `pay_no`, `pay_time`, " +
                "`service_subject`, `service_project_type`, `contact_person`, `phone`, `address`, " +
                "`num`, `exp_com_time`, `unit_price`, `cd_reason`, `total_amount`, " +
                "`shop_img`, `crop`, `user_id`, `express_com`, `express_num`, `del_flag`" +
                ") VALUES (" +
                "?, NULL, NULL, ?, NULL, " +
                "NULL, NULL, NULL, ?, ?, " +
                "NULL, NULL, ?, ?, NULL, " +
                "?, ?, NULL, NULL, NULL, " +
                "?, ?, ?, ?, ?, " +
                "?, ?, ?, NULL, ?, " +
                "?, ?, ?, NULL, NULL, ?" +
                ")";

        try (Connection conn = dataSource.getConnection()) {
            conn.setAutoCommit(false);
            try (PreparedStatement ps = conn.prepareStatement(insertSql)) {
                for (OrderInsertData data : batchData) {
                    int paramIdx = 1;
                    // 1. 主键与基础状态
                    ps.setString(paramIdx++, data.getId());
                    ps.setInt(paramIdx++, ALLOT_STATUS);
                    // 2. 创建信息
                    ps.setString(paramIdx++, CREATE_BY);
                    ps.setTimestamp(paramIdx++, data.getCreateTime());
                    // 3. 组织与服务信息
                    ps.setString(paramIdx++, SYS_ORG_CODE);
                    ps.setString(paramIdx++, data.getServiceProject());
                    ps.setInt(paramIdx++, ORDER_TYPE);
                    ps.setInt(paramIdx++, ORDER_STATUS);
                    // 4. 服务主体与类型
                    ps.setString(paramIdx++, SERVICE_SUBJECT);
                    ps.setInt(paramIdx++, SERVICE_PROJECT_TYPE);
                    // 5. 联系人与地址
                    ps.setString(paramIdx++, data.getContactPerson());
                    ps.setString(paramIdx++, data.getPhone());
                    ps.setString(paramIdx++, data.getAddress());
                    // 6. 数量与时间
                    ps.setInt(paramIdx++, data.getNum());
                    ps.setTimestamp(paramIdx++, data.getExpComTime());
                    ps.setBigDecimal(paramIdx++, UNIT_PRICE);
                    // 7. 金额与图片
                    ps.setBigDecimal(paramIdx++, data.getTotalAmount());
                    ps.setString(paramIdx++, data.getShopImg());
                    // 8. 作物与用户
                    ps.setString(paramIdx++, data.getCrop());
                    ps.setString(paramIdx++, USER_ID);
                    // 9. 删除标记
                    ps.setInt(paramIdx++, DEL_FLAG);

                    ps.addBatch();
                }

                // 执行批处理
                int[] affectedRows = ps.executeBatch();
                conn.commit();

                // 记录ID到回滚文件
                synchronized (writer) {
                    for (OrderInsertData data : batchData) {
                        writer.write(data.getId());
                        writer.newLine();
                    }
                }

                int totalAffected = Arrays.stream(affectedRows).sum();
                logger.info("批次插入完成：处理{}条，影响{}行", batchData.size(), totalAffected);
                return totalAffected;
            } catch (SQLException e) {
                conn.rollback();
                logger.error("批次插入失败，已回滚", e);
                throw e;
            }
        }
    }

    /**
     * 主批量插入方法
     */
    public static void performBatchInsert() throws SQLException, InterruptedException, IOException {
        // 1. 生成水稻数据
        List<OrderInsertData> allData = generateInsertDataList();
        if (allData.isEmpty()) {
            logger.warn("无待插入数据，退出");
            return;
        }

        // 2. 初始化线程池与计数器
        ExecutorService executor = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
        AtomicLong totalAffected = new AtomicLong(0);
        // 使用try-with-resources自动管理流
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(DATA_FILE_PATH))) {

            // 3. 分批次提交任务
            int totalBatches = (int) Math.ceil((double) allData.size() / BATCH_SIZE);
            for (int i = 0; i < totalBatches; i++) {
                int start = i * BATCH_SIZE;
                int end = Math.min(start + BATCH_SIZE, allData.size());
                List<OrderInsertData> batch = allData.subList(start, end);

                executor.submit(() -> {
                    try {
                        int affected = processInsertBatch(batch, writer);
                        totalAffected.addAndGet(affected);
                    } catch (SQLException | IOException e) {
                        logger.error("批次插入失败，重试1次", e);
                        try {
                            Thread.sleep(1000);
                            int affected = processInsertBatch(batch, writer);
                            totalAffected.addAndGet(affected);
                            logger.info("批次重试成功");
                        } catch (SQLException | IOException | InterruptedException ex) {
                            logger.error("批次重试失败，数据未插入", ex);
                        }
                    }
                });
            }

            // 4. 等待所有任务完成
            executor.shutdown();
            long waitTime = 0;
            long interval = 1000;
            while (!executor.awaitTermination(interval, TimeUnit.MILLISECONDS)) {
                waitTime += interval;
                logger.info("等待剩余任务完成...已等待{}ms", waitTime);
                if (waitTime > 5 * 60 * 1000) { // 5分钟超时
                    logger.warn("等待超时，强制关闭线程池");
                    executor.shutdownNow();
                    break;
                }
            }
        } finally {
            if (!executor.isTerminated()) {
                executor.shutdownNow();
            }
        }

        // 5. 输出结果
        logger.info("水稻批量插入完成！总记录数：{}，总影响行数：{}，总金额：{}",
                allData.size(), totalAffected.get(),
                UNIT_PRICE.multiply(new BigDecimal(allData.size() * BASE_NUM_PER_RECORD)));
    }

    /**
     * 回滚插入的数据（删除）
     */
    public static void rollbackInsertedData() throws IOException, SQLException, InterruptedException {
        // 1. 读取回滚ID
        List<String> ids = new ArrayList<>();
        try (BufferedReader reader = new BufferedReader(new FileReader(DATA_FILE_PATH))) {
            String line;
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                if (!line.isEmpty()) ids.add(line);
            }
        }
        if (ids.isEmpty()) {
            logger.warn("无回滚ID，退出");
            return;
        }

        // 2. 分批次删除
        ExecutorService executor = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
        AtomicLong totalDeleted = new AtomicLong(0);
        int totalBatches = (int) Math.ceil((double) ids.size() / BATCH_SIZE);

        for (int i = 0; i < totalBatches; i++) {
            int start = i * BATCH_SIZE;
            int end = Math.min(start + BATCH_SIZE, ids.size());
            List<String> batchIds = ids.subList(start, end);

            executor.submit(() -> {
                try {
                    int deleted = processRollbackBatch(batchIds);
                    totalDeleted.addAndGet(deleted);
                } catch (SQLException e) {
                    logger.error("批次回滚失败，重试1次", e);
                    try {
                        Thread.sleep(1000);
                        int deleted = processRollbackBatch(batchIds);
                        totalDeleted.addAndGet(deleted);
                        logger.info("批次回滚重试成功");
                    } catch (SQLException | InterruptedException ex) {
                        logger.error("批次回滚重试失败", ex);
                    }
                }
            });
        }

        // 3. 关闭资源
        executor.shutdown();
        if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
            executor.shutdownNow();
            logger.warn("线程池超时，强制关闭");
        }

        logger.info("回滚完成！删除记录数：{}", totalDeleted.get());
    }

    /**
     * 处理单个批次回滚
     */
    private static int processRollbackBatch(List<String> batchIds) throws SQLException {
        if (batchIds.isEmpty()) return 0;

        // 构建IN条件
        StringBuilder inClause = new StringBuilder();
        for (int i = 0; i < batchIds.size(); i++) {
            inClause.append("?");
            if (i < batchIds.size() - 1) inClause.append(",");
        }
        String deleteSql = "DELETE FROM `yilong`.`service_order` WHERE `id` IN (" + inClause + ")";

        try (Connection conn = dataSource.getConnection()) {
            conn.setAutoCommit(false);
            try (PreparedStatement ps = conn.prepareStatement(deleteSql)) {
                for (int i = 0; i < batchIds.size(); i++) {
                    ps.setString(i + 1, batchIds.get(i));
                }

                int affected = ps.executeUpdate();
                conn.commit();
                logger.info("批次回滚：删除{}条，影响{}行", batchIds.size(), affected);
                return affected;
            } catch (SQLException e) {
                conn.rollback();
                logger.error("批次回滚失败，已回滚", e);
                throw e;
            }
        }
    }

    // ============================ 10. 程序入口 ============================
    public static void main(String[] args) {
        try {
            // 执行批量插入
            performBatchInsert();

            // 执行回滚（需要时解开注释）
            // rollbackInsertedData();
        } catch (Exception e) {
            logger.error("程序执行失败", e);
        }
    }
}
